<?php


namespace App\Service\Cooperative;


use App\Constants\ContextKey;
use App\Constants\ErrorCode;
use App\Dao\CooperativeDao;
use App\Dao\CooperativeMenuDao;
use App\Dao\CooperativeRoleDao;
use App\Dao\CooperativeUserDao;
use App\Exception\BusinessException;
use App\Model\Cooperative;
use App\Model\CooperativeRole;
use App\Model\CooperativeUser;
use App\Utils\Captcha;
use App\Utils\Casbin;
use App\Utils\Jwt;
use App\Utils\Log;
use App\Utils\SMS;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\Context;

class CooperativeUserService extends BaseService
{

    /**
     * @Inject()
     * @var CooperativeUserDao
     */
    protected $cooperativeUserDao;

    /**
     * @Inject()
     * @var CooperativeMenuDao
     */
    protected $cooperativeMenuDao;

    /**
     * @Inject()
     * @var CooperativeRoleDao
     */
    protected $cooperativeRoleDao;

    /**
     * @Inject()
     * @var CooperativeDao
     */
    protected $cooperativeDao;

    /**
     * @var Casbin
     */
    protected $casbinInstance;

    public function __construct()
    {
        $this->casbinInstance = make(Casbin::class, [config('casbin.cooperative')]);
    }

    /**
     * @param array $where
     * @return array
     */
    public function index(array $where): array
    {
        if ($where['role_id'] != 0) {
            $userIds = $this->casbinInstance->getUsersByRole($where['role_id']);

            $where['ids'] = empty($userIds) ? [''] : $userIds;
        }
        $where['cooperative_id'] = $this->user()->cooperative_id;
        unset($where['role_id']);
        return $this->formatIndex($this->cooperativeUserDao->where($where)->paginate());
    }

    public function formatIndex(\Hyperf\Contract\LengthAwarePaginatorInterface $index): array
    {

        $data = [];
        /** @var CooperativeUser $cooperativeUser */
        foreach ($index->items() as $cooperativeUser) {
            $roleId = $this->casbinInstance->getRolesByUser($cooperativeUser->id)[0];

            /** @var CooperativeRole $role */
            $role = $this->cooperativeRoleDao->firstOrThrow(['id' => $roleId]);
            $data[] = [
                'id' => $cooperativeUser->id,
                'mobile' => $cooperativeUser->mobile,
                'username' => $cooperativeUser->username,
                'status' => $cooperativeUser->status,
                'position' => $cooperativeUser->position,
                'role_id' => $role->id,
                'role_name' => $role->name,
                'created_at' => $cooperativeUser->created_at->toDateTimeString(),
            ];
        }

        return [
            'data' => $data,
            'current_page' => $index->currentPage(),
            'total' => $index->total(),
            'per_page' => $index->perPage()
        ];
    }

    /**
     * @param $id
     * @param array $data
     * @return CooperativeUser
     * @throws \Exception
     */
    public function save($id, array $data): CooperativeUser
    {
        if ($id) {
            /** @var CooperativeUser $cooperativeUser */
            $cooperativeUser = $this->cooperativeUserDao->first(['id' => $id, 'cooperative_id' => $this->user()->cooperative_id]);
            if (!$cooperativeUser) {
                throw new BusinessException(ErrorCode::COOPERATIVE_USER_NOT_FOUND);
            }
            if ($cooperativeUser->isManager()) {
                throw new BusinessException(ErrorCode::COOPERATIVE_USER_MANAGER_CANT_CHANGE);
            }
        } else {
            $cooperativeUser = new CooperativeUser();
        }
        $cooperativeUser->password = password_hash($data['password'], PASSWORD_BCRYPT);
        $cooperativeUser->fill($data);

        /**
         * @var $currentUer CooperativeUser
         */
        $currentUer = $this->user();
        $cooperativeUser->cooperative_id = $currentUer->cooperative_id;

        //该角色是否存在 且 属于该合作商
        $exists = $this->cooperativeRoleDao->where(
            ['id' => $data['role_id'], 'cooperative_id' => $cooperativeUser->cooperative_id]
        )->exists();
        if (!$exists) {
            throw new BusinessException(ErrorCode::COOPERATIVE_ROLE_NOT_FOUND);
        }

        Db::beginTransaction();
        try {
            $cooperativeUser->save();
            $this->casbinInstance->setRolesForUser($cooperativeUser->id, [$data['role_id']]);
            Db::commit();
        } catch (\Exception $exception) {
            Db::rollBack();
            Log::get()->error($exception->getMessage(), $data);
            throw $exception;
        }
        return $cooperativeUser;
    }

    /**
     * @param int $id
     */
    public function delete(int $id): void
    {
        $cooperativeUser = $this->cooperativeUserDao->first(['id' => $id, 'cooperative_id' => $this->user()->cooperative_id]);

        if (!$cooperativeUser) {
            throw new BusinessException(ErrorCode::COOPERATIVE_USER_NOT_FOUND);
        }

        if ($cooperativeUser->isManager()) {
            throw new BusinessException(ErrorCode::COOPERATIVE_USER_MANAGER_CANT_DEL);
        }

        Db::beginTransaction();
        try {
            $this->cooperativeUserDao->delete($id);
            $this->casbinInstance->deleteUser($id);
            Db::commit();
        } catch (\Exception $exception) {
            Db::rollBack();
            Log::get()->error($exception->getMessage(), ['cooperative_user_id' => $id]);
            throw $exception;
        }

    }

    /**
     * @param array $data
     * @return string
     */
    public function login(array $data): array
    {
        switch ($data['type']) {
            case \App\Constants\Model\CooperativeUser::LOGIN_WITH_PASSWORD:
                $cooperativeUser = $this->loginWithPassword($data['phone'], $data['password'], $data['captcha_key'], $data['captcha_code']);
                break;
            case \App\Constants\Model\CooperativeUser::LOGIN_WITH_CODE:
                $cooperativeUser = $this->loginWithCode($data['phone'], $data['code']);
                break;
        }
        /** @var Cooperative $cooperative */
        $cooperative = $this->cooperativeDao->where(['id' => $cooperativeUser->cooperative_id])->first();
        if($cooperative->status == \App\Constants\Model\Cooperative::STATUS_BLOCKED){
            throw new BusinessException(ErrorCode::COOPERATIVE_DISABLE);
        }

        $roleId = $this->casbinInstance->getRolesByUser($cooperativeUser->id)[0];
        /** @var CooperativeRole $role */
        $role = $this->cooperativeRoleDao->firstOrThrow(['id' => $roleId]);
        $menus = $role->cooperativeMenus;
        $tree = $this->cooperativeMenuDao->tree($menus);
        return [
            'token' => Jwt::token(['id' => $cooperativeUser->id], 'jwt.cooperative'),
            'cooperative_user' => $cooperativeUser->toArray(),
            'cooperative' => [
                'contact' => $cooperative->contact,
                'phone' => $cooperative->phone,
                'domain' => $cooperative->domain,
                'nickname' => $cooperative->nickname,
                'wx_merchant_secret' => $cooperative->wx_merchant_secret,
                'wx_merchant_code' => $cooperative->wx_merchant_code,
                'wx_cert' => $cooperative->wx_cert,
                'audit_status' => $cooperative->audit_status,
                'audit_message' => $cooperative->audit_message,
                'province' => $cooperative->province,
                'city' => $cooperative->city,
            ],
            'menu' => $tree
        ];
    }


    /**
     * @param string $phone
     * @param string $password
     * @return \Hyperf\Database\Model\Builder|\Hyperf\Database\Model\Model|object
     */
    public function loginWithPassword(string $phone, string $password, string $captchaKey, string $captchaCode): CooperativeUser
    {
        $captcha = di(Captcha::class);

        $captcha->checkCaptcha($captchaKey, $captchaCode);

        $cooperativeUser = $this->cooperativeUserDao->first(['mobile' => $phone]);

        //用户不存在
        if (!$cooperativeUser) {
            throw new BusinessException(ErrorCode::COOPERATIVE_USER_NOT_FOUND);
        }

        //密码错误
        if (!password_verify($password, $cooperativeUser->password)) {
            throw new BusinessException(ErrorCode::COOPERATIVE_USER_PASSWORD_FAILED);
        }

        return $cooperativeUser;
    }

    /**
     * @param string $phone
     * @param string $code
     * @return \Hyperf\Database\Model\Builder|\Hyperf\Database\Model\Model|object
     */
    public function loginWithCode(string $phone, string $code): CooperativeUser
    {

        switch (env('APP_ENV')) {
            case 'local':
                if ($code != '123456') {
                    throw new BusinessException(ErrorCode::SMS_CAPTCHA_UNEQUAL);
                }
                break;
            default:
                if (!di(SMS::class)->checkCaptcha($phone, $code)) {
                    throw new BusinessException(ErrorCode::SMS_CAPTCHA_UNEQUAL);
                }
        }

        $cooperativeUser = $this->cooperativeUserDao->first(['mobile' => $phone]);

        //用户不存在
        if (!$cooperativeUser) {
            throw new BusinessException(ErrorCode::COOPERATIVE_USER_NOT_FOUND);
        }

        return $cooperativeUser;
    }

    /**
     * @return CooperativeUser
     */
    public function me(): CooperativeUser
    {
        /** @var  $cooperativeUser CooperativeUser */
        $cooperativeUser = $this->user();
        return $cooperativeUser;
    }

    /**
     * @param array $data
     */
    public function saveMe(array $data)
    {
        /** @var  $cooperativeUser CooperativeUser */
        $cooperativeUser = $this->user();

        if (isset($data['username']) && !empty($data['username'])) {
            $cooperativeUser->username = $data['username'];
        }

        if (isset($data['avatar']) && !empty($data['avatar'])) {
            $cooperativeUser->avatar = $data['avatar'];
        }

        if (isset($data['contact']) && !empty($data['contact'])) {
            $cooperativeUser->contact = $data['contact'];
        }

        if (isset($data['password']) && !empty($data['password'])) {
            if ($data['password'] != $data['repassword']) {
                throw  new BusinessException(ErrorCode::COOPERATIVE_USER_REPASSWORD_FAILED);
            }
            $cooperativeUser->password = password_hash($data['password'], PASSWORD_BCRYPT);
        }
        $cooperativeUser->save();
    }

    /**
     * 是否接收客服
     * @param int $id
     * @return bool
     */
    public function setIsCust(int $id)
    {
        $result = $this->cooperativeUserDao->first(['id' => $id]);
        if ($result->is_cust) {
            $result->is_cust = 0;
        } else {
            $result->is_cust = 1;
        }
        return $result->save();
    }
}